﻿using JNPF.Data.Util;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;

namespace JNPF.Data
{
    /// <summary>
    /// 操作实现
    /// 版 本：V3.0.0
    /// 版 权：引迈信息技术有限公司（https://www.jnpfsoft.com）
    /// 作 者：JNPF开发平台组
    /// 日 期：2017.03.03
    /// </summary>
    public class AbstractDbAccessor : IDatabase, IDatabaseExt
    {
        #region 构造函数
        public BaseDbContext dbContext { get; set; }
        public AbstractDbAccessor(string connString, DatabaseType dbType)
        {
            dbContext = new BaseDbContext(connString, dbType);
        }
        #endregion

        #region 事物相关
        protected IDbContextTransaction _transaction { get; set; }
        public void BeginTransaction()
        {
            if (dbContext.Database.CurrentTransaction == null)
            {
                _transaction = dbContext.Database.BeginTransaction();
            }
        }
        public async Task BeginTransactionAsync()
        {
            if (dbContext.Database.CurrentTransaction == null)
            {
                _transaction = await dbContext.Database.BeginTransactionAsync();
            }
        }
        public void CommitTransaction()
        {
            _transaction?.Commit();
        }
        public async Task CommitTransactionAsync()
        {
            await _transaction?.CommitAsync();
        }
        public void RollbackTransaction()
        {
            _transaction?.Rollback();
        }
        public async Task RollbackTransactionAsync()
        {
            await _transaction?.RollbackAsync();
        }
        #endregion

        #region 增加数据
        public int Insert<T>(T entity) where T : class, new()
        {
            return Insert(new List<T> { entity });
        }
        public async Task<int> InsertAsync<T>(T entity) where T : class, new()
        {
            return await InsertAsync(new List<T> { entity });
        }
        public int Insert<T>(List<T> entities) where T : class, new()
        {
            dbContext.AddRange(entities);
            return dbContext.SaveChanges();
        }
        public async Task<int> InsertAsync<T>(List<T> entities) where T : class, new()
        {
            await dbContext.AddRangeAsync(entities);
            return await dbContext.SaveChangesAsync();
        }
        public int BulkInsert(DataTable dt)
        {
            return dbContext.Database.GetDbConnection().SqlBulkCopy(dt);
        }
        #endregion

        #region 删除数据
        public int Delete<T>(T entity) where T : class, new()
        {
            return Delete(new List<T> { entity });
        }
        public async Task<int> DeleteAsync<T>(T entity) where T : class, new()
        {
            return await DeleteAsync(new List<T> { entity });
        }
        public int Delete<T>(List<T> entities) where T : class, new()
        {
            dbContext.RemoveRange(entities);
            return dbContext.SaveChanges();
        }
        public async Task<int> DeleteAsync<T>(List<T> entities) where T : class, new()
        {
            dbContext.RemoveRange(entities);
            return await dbContext.SaveChangesAsync();
        }
        public int Delete<T>(Expression<Func<T, bool>> condition) where T : class, new()
        {
            var deleteList = GetIQueryable<T>().Where(condition).ToList();
            return Delete(deleteList);
        }
        public async Task<int> DeleteAsync<T>(Expression<Func<T, bool>> condition) where T : class, new()
        {
            var deleteList = await GetIQueryable<T>().Where(condition).ToListAsync();
            return await DeleteAsync(deleteList);
        }
        public int DeleteAll<T>() where T : class, new()
        {
            var table = typeof(T).GetCustomAttribute<TableAttribute>().Name;
            var sql = DbSqlBuilder.DeleteSql(table).ToString();
            return ExecuteSql(sql);
        }
        public async Task<int> DeleteAllAsync<T>() where T : class, new()
        {
            var table = typeof(T).GetCustomAttribute<TableAttribute>().Name;
            var sql = DbSqlBuilder.DeleteSql(table).ToString();
            return await ExecuteSqlAsync(sql);
        }
        #endregion

        #region 更新数据
        public int Update<T>(T entity) where T : class, new()
        {
            return Update(new List<T> { entity });
        }
        public async Task<int> UpdateAsync<T>(T entity) where T : class, new()
        {
            return await UpdateAsync(new List<T> { entity });
        }
        public int Update<T>(List<T> entities) where T : class, new()
        {
            var keyName = dbContext.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties.Select(x => x.Name).Single();
            entities.ForEach(aEntity =>
            {
                aEntity.GetType().GetProperties().ForEach(p =>
                {
                    if (p.GetValue(aEntity) != null && p.Name != keyName)
                    {
                        dbContext.Entry(aEntity).Property(p.Name).IsModified = true;
                    }
                });
            });
            return dbContext.SaveChanges();
        }
        public async Task<int> UpdateAsync<T>(List<T> entities) where T : class, new()
        {
            var keyName = dbContext.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties.Select(x => x.Name).Single();
            entities.ForEach(aEntity =>
            {
                aEntity.GetType().GetProperties().ForEach(p =>
                {
                    if (p.GetValue(aEntity) != null && p.Name != keyName)
                    {
                        dbContext.Entry(aEntity).Property(p.Name).IsModified = true;
                    }
                });
            });
            return await dbContext.SaveChangesAsync();
        }
        //public int UpdateAny<T>(T entity, List<string> properties) where T : class, new()
        //{
        //    return UpdateAny(new List<T> { entity }, properties);
        //}
        //public async Task<int> UpdateAnyAsync<T>(T entity, List<string> properties) where T : class, new()
        //{
        //    return await UpdateAnyAsync(new List<T> { entity }, properties);
        //}
        //public int UpdateAny<T>(List<T> entities, List<string> properties) where T : class, new()
        //{
        //    entities.ForEach(aEntity =>
        //    {
        //        properties.ForEach(aProperty =>
        //        {
        //            dbContext.Entry(aEntity).Property(aProperty).IsModified = true;
        //        });
        //    });
        //    return dbContext.SaveChanges();
        //}
        //public async Task<int> UpdateAnyAsync<T>(List<T> entities, List<string> properties) where T : class, new()
        //{
        //    entities.ForEach(aEntity =>
        //    {
        //        properties.ForEach(aProperty =>
        //        {
        //            dbContext.Entry(aEntity).Property(aProperty).IsModified = true;
        //        });
        //    });
        //    return await dbContext.SaveChangesAsync();
        //}
        public int UpdateWhere<T>(Expression<Func<T, bool>> condition, Action<T> set) where T : class, new()
        {
            var list = GetIQueryable<T>().Where(condition).ToList();
            list.ForEach(aData => set(aData));
            return Update(list);
        }
        public async Task<int> UpdateWhereAsync<T>(Expression<Func<T, bool>> condition, Action<T> set) where T : class, new()
        {
            var list = GetIQueryable<T>().Where(condition).ToList();
            list.ForEach(aData => set(aData));
            return await UpdateAsync(list);
        }
        #endregion

        #region 查询数据
        public T GetEntity<T>(params object[] keyValue) where T : class, new()
        {
            var obj = dbContext.Set<T>().Find(keyValue);
            if (!obj.IsNullOrEmpty())
                dbContext.Entry(obj).State = EntityState.Detached;
            return obj;
        }
        public async Task<T> GetEntityAsync<T>(params object[] keyValue) where T : class, new()
        {
            var obj = await dbContext.Set<T>().FindAsync(keyValue);
            if (!obj.IsNullOrEmpty())
                dbContext.Entry(obj).State = EntityState.Detached;
            return obj;
        }
        public T GetEntity<T>(Expression<Func<T, bool>> condition) where T : class, new()
        {
            return dbContext.Set<T>().Where(condition).FirstOrDefault();
        }
        public async Task<T> GetEntityAsync<T>(Expression<Func<T, bool>> condition) where T : class, new()
        {
            var list = await GetIQueryable<T>().Where(condition).ToListAsync();
            return list.FirstOrDefault();
        }
        public IQueryable<T> GetIQueryable<T>() where T : class, new()
        {
            return dbContext.GetIQueryable(typeof(T)) as IQueryable<T>;
        }
        public List<T> GetList<T>() where T : class, new()
        {
            return GetIQueryable<T>().ToList();
        }
        public async Task<List<T>> GetListAsync<T>() where T : class, new()
        {
            return await GetIQueryable<T>().ToListAsync();
        }
        public List<T> GetList<T>(Expression<Func<T, bool>> condition) where T : class, new()
        {
            return GetIQueryable<T>().Where(condition).ToList();
        }
        public async Task<List<T>> GetListAsync<T>(Expression<Func<T, bool>> condition) where T : class, new()
        {
            return await GetIQueryable<T>().Where(condition).ToListAsync();
        }
        public List<T> GetListSql<T>(string sql, DbParameter[] param = null) where T : class, new()
        {
            return dbContext.Database.GetDbConnection().ExecuteReader(sql, param).ToList<T>();
        }
        public async Task<List<T>> GetListSqlAsync<T>(string sql, DbParameter[] param = null) where T : class, new()
        {
            var reader = await dbContext.Database.GetDbConnection().ExecuteReaderAsync(sql, param);
            return reader.ToList<T>();
        }
        public DataTable GetTableSql(string sql, DbParameter[] param = null)
        {
            return dbContext.Database.GetDbConnection().ExecuteReader(sql, param).ToTable();
        }
        public async Task<DataTable> GetTableSqlAsync(string sql, DbParameter[] param = null)
        {
            var reader = await dbContext.Database.GetDbConnection().ExecuteReaderAsync(sql, param);
            return reader.ToTable();
        }
        public async Task<DataTable> GetTableSqlByUnconvertAsync(string sql, DbParameter[] param = null)
        {
            var reader = await dbContext.Database.GetDbConnection().ExecuteReaderAsync(sql, param);
            return reader.ToTableByUnconvert();
        }
        public int GetCount<T>(Expression<Func<T, bool>> condition = null) where T : class, new()
        {
            return GetIQueryable<T>().Where(condition).Count();
        }
        public async Task<int> GetCountAsync<T>(Expression<Func<T, bool>> condition = null) where T : class, new()
        {
            return await GetIQueryable<T>().Where(condition).CountAsync();
        }
        #endregion

        #region 执行Sql语句
        public int ExecuteSql(string sql, DbParameter[] param = null)
        {
            if (param == null)
                return dbContext.Database.ExecuteSqlRaw(sql);
            else
                return dbContext.Database.ExecuteSqlRaw(sql, param);
        }
        public async Task<int> ExecuteSqlAsync(string sql, params DbParameter[] param)
        {
            if (param == null)
                return await dbContext.Database.ExecuteSqlRawAsync(sql);
            else
                return await dbContext.Database.ExecuteSqlRawAsync(sql, param);
        }
        public object ExecuteScalar(string sql, DbParameter[] param = null)
        {
            return dbContext.Database.GetDbConnection().ExecuteScalar(sql, param);
        }
        public async Task<object> ExecuteScalarAsync(string sql, DbParameter[] param = null)
        {
            return await dbContext.Database.GetDbConnection().ExecuteScalarAsync(sql, param);
        }

        public async Task<List<Dictionary<string, object>>> GetPageDictionaryAsync(string sql, DbParameter[] param = null)
        {
            List<Dictionary<string, object>> list = new List<Dictionary<string, object>>();
            var reader = await dbContext.Database.GetDbConnection().ExecuteReaderAsync(sql, param);
            using (reader)
            {
                while (reader.Read())
                {
                    list.Add(DataFillDictionary(reader));
                }
            }
            return list;
        }

        /// <summary>
        /// 将DataReader数据转为Dictionary<string, object>对象
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        public static Dictionary<string, object> DataFillDictionary(IDataReader reader)
        {
            Dictionary<string, object> dict = new Dictionary<string, object>();
            for (int i = 0; i < reader.FieldCount; i++)
            {
                try
                {
                    dict.Add(reader.GetName(i), reader.GetValue(i) is DBNull ? null : reader.GetValue(i));
                }
                catch
                {
                    dict.Add(reader.GetName(i), null);
                }
            }
            return dict;
        }
        #endregion

        #region Dispose
        public virtual void Dispose()
        {
            if (_transaction != null)
                _transaction.Dispose();
            dbContext.Dispose();
        }
        #endregion
    }
}
